<?php
/**
 * 验证码类
 *
 * [Code]
 * $h = App::h('Captcha');
 * $h->display(1, array('bordercolor'=>'#C5C5C5', 'width'=>148, 'height'=>26));
 * [/Code]
 *
 * @version $Id: Captcha.php 90 2013-05-21 08:40:21Z linsir123 $
 * @package Helper
 */
class HelperCaptcha
{
	/**
	 * 选项
	 *
	 * @var array
	 */
	public $options = array(
		'bordercolor' => null,
		'width' => 70,
		'height' => 21,
		'length' => 4,
	);

	/**
	 * 验证码
	 *
	 * @var string
	 */
	public $code;

	/**
	 * 时期时间
	 *
	 * @var int
	 */
	public $expired;

	/**
	 * 图像句柄
	 *
	 * @var resource
	 */
	private $_im;

	/**
	 * 图像宽度
	 *
	 * @var int
	 */
	private $_width;

	/**
	 * 图像高度
	 *
	 * @var int
	 */
	private $_height;

	/**
	 * Key
	 *
	 * @var string
	 */
	private $_codeKey;

	/**
	 * Key
	 *
	 * @var string
	 */
	private $_expiredKey;

	/**
	 * 构造函数
	 */
	public function init($prefix = "Captcha")
	{
		@session_start();

		///
		$this->_codeKey = $prefix . "Code";
		$this->_expiredKey = $prefix . "Expired";
		$this->code = isset($_SESSION[$this->_codeKey]) ? $_SESSION[$this->_codeKey] : '';
		$this->expired = isset($_SESSION[$this->_expiredKey]) ? $_SESSION[$this->_expiredKey] : 0;
	}

	/**
	 * 生成图像
	 *
	 * @param int $type 生成的内容分类
	 * @param array $options 图像选项
	 */
	public function display($type = 0, $options = array())
	{
		$this->_generateCode($type);

		///
		if (is_array($options))
			$this->options = array_merge($this->options, $options);

		$this->_width = intval($this->options['width']);
		$this->_height = intval($this->options['height']);
		$this->_makeBackground();
		$this->_makeFont();

		///
		header('Content-type:image/png');
		imagepng($this->_im);
		imagedestroy($this->_im);
	}

	/**
	 * 检验验证码是否输入正确
	 *
	 * @param string $code 用户输入的验证码
	 *
	 * @return bool
	 */
	public function check($code)
	{
		return (time() < $this->expired && strtoupper($code) == strtoupper($this->code));
	}

	/**
	 * 生成背景
	 */
	private function _makeBackground()
	{
		///
		$this->_im = imagecreate($this->_width, $this->_height);

		///
		$c = $step = array();
		for ($i = 0; $i < 3; $i ++) {
			$start[$i] = mt_rand(200, 255);
			$end[$i] = mt_rand(100, 150);
			$step[$i] = ($end[$i] - $start[$i]) / $this->_width;
			$c[$i] = $start[$i];
		}
		for ($i = 0; $i < $this->_width; $i ++) {
			$color = imagecolorallocate($this->_im, $c[0], $c[1], $c[2]);
			imageline($this->_im, $i, 0, $i, $this->_height, $color);
			$c[0] += $step[0];
			$c[1] += $step[1];
			$c[2] += $step[2];
		}

		///
		$linenums = $this->_height / 10;
		for ($i = 0; $i <= $linenums; $i ++) {
			$color = imagecolorallocate($this->_im, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
			$x = mt_rand(0, $this->_width);
			$y = mt_rand(0, $this->_height);
			imagearc($this->_im, $x, $y, mt_rand(0, $this->_width), mt_rand(0, $this->_height), mt_rand(0, 360), mt_rand(0, 360), $color);
		}

		///
		if ($this->options['bordercolor']) {
			list($r, $g, $b) = $this->_hex2rgb($this->options['bordercolor']);
			$color = imagecolorallocate($this->_im, $r, $g, $b);
			imagerectangle($this->_im, 0, 0, $this->_width - 1, $this->_height - 1, $color);
		}
	}

	/**
	 * 生成字体
	 */
	private function _makeFont()
	{
		///
		$width = 0;
		$font = array();
		$seccode = $this->code;
		$len = strlen($seccode);
		for ($i = 0; $i < $len; $i ++) {
			$font[$i]['size'] = 12;
			$font[$i]['font'] = dirname(__FILE__) . DS . 'fonts/verdanab.ttf';

			///
			$box = imagettfbbox($font[$i]['size'], 0, $font[$i]['font'], $seccode[$i]);
			$font[$i]['height'] = max($box[1], $box[3]) - min($box[5], $box[7]);
			$font[$i]['width'] = (max($box[2], $box[4]) - min($box[0], $box[6])) + mt_rand(0, $this->_width / 8);
			$font[$i]['width'] = $font[$i]['width'] > $this->_width / $len ? $this->_width / $len : $font[$i]['width'];
			$width += $font[$i]['width'];
		}

		///
		$x = mt_rand(1, $this->_width - $width);
		for ($i = 0; $i < $len; $i ++) {
			$fontColor = array(mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
			$shadowColor = imagecolorallocate($this->_im, 255 - $fontColor[0], 255 - $fontColor[1], 255 - $fontColor[2]);
			$textColor = imagecolorallocate($this->_im, $fontColor[0], $fontColor[1], $fontColor[2]);
			$y = mt_rand($font[$i]['height'] + 2, $this->_height - 5);
			imagettftext($this->_im, $font[$i]['size'], 0, $x + 1, $y + 1, $shadowColor, $font[$i]['font'], $seccode[$i]);
			imagettftext($this->_im, $font[$i]['size'], 0, $x, $y, $textColor, $font[$i]['font'], $seccode[$i]);
			$x += $font[$i]['width'];
		}
	}

	/**
	 * 成生验证码
	 *
	 * @param int $type 生成的内容分类
	 */
	private function _generateCode($type)
	{
		///
		switch ($type) {
			case 1:
				$seed = '0123456789';
				break;
			case 2:
				$seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
				break;
			default:
				$seed = '012346789ABCDEFGHJKLMNPQRTUVWXYZabcdefghijklmnopqrstuvwxyz';
				break;
		}

		///
		$this->code = '';
		list($usec, $sec) = explode(' ', microtime());
		srand($sec + $usec * 100000);
		for ($i = 0; $i < $this->options['length']; $i ++)
			$this->code .= substr($seed, rand(0, strlen($seed) - 1), 1);

		///
		$_SESSION[$this->_codeKey] = $this->code;
		$_SESSION[$this->_expiredKey] = $this->expired = time() + 900;
	}

	/**
	 * 将16进制颜色值转成RGB格式
	 * // `Captcha.php`,`Image.php`组件有用到
	 *
	 * @param string $color 颜色值(16进制)
	 * @param string $defualt 默认颜色值(16进制)
	 *
	 * @return array
	 */
	private function _hex2rgb($color, $defualt = 'ffffff')
	{
		///
		$color = strtolower($color);
		if (substr($color, 0, 2) == '0x')
			$color = substr($color, 2);
		elseif (substr($color, 0, 1) == '#')
			$color = substr($color, 1);

		///
		$len = strlen($color);
		if ($len == 3)
			return array(
				hexdec(substr($color, 0, 1)),
				hexdec(substr($color, 1, 1)),
				hexdec(substr($color, 2, 1))
			);
		elseif ($len != 6)
			$color = $defualt;

		///
		return array(
			hexdec(substr($color, 0, 2)),
			hexdec(substr($color, 2, 2)),
			hexdec(substr($color, 4, 2))
		);
	}
}
